#include <stdio.h>
#include <tool/yoneWayList.h>

/** One Way list item struct, declare beforehand for use */
typedef struct oneWayListItem oneWayListItem;

/** One Way list item struct */
struct oneWayListItem {
  oneWayListItem *next;
  void *anItem;
};

/** One Way list data struct */
struct oneWayListData {
  /** first (head), last (tail), previous & current position */
  oneWayListItem *first, *prev, *cur;
  /** Number of items in the list, NOT using "unsigned int" 
  require -1 value. */
  int n;
  unsigned char sizeof_oneWayListItem;
  /*--- provide customized function pointers */
  /** This method is NOT provided, require customized function.
  Required to provide this when you create the one way list. */
  boolean (*freeItem)(void **anItemP);
  /** This method is NOT provided, require customized function.
  This is NOT a compulsory function pointer. */
  void *(*itemClone)(const void *anItem, const void *aControlObj);
  /** This method is NOT provided, require customized function.
  This is NOT a compulsory function pointer. */
  g_reply (*compareItem)(const void *anItem1, const void *anItem2);
};

/*======= declare internal function */
boolean yoneWayList_free(oneWayList *listP);
int yoneWayList_size(const oneWayList list);
boolean yoneWayList_clear(oneWayList list);
/*--- traverse */
boolean yoneWayList_previous(const oneWayList list);
boolean yoneWayList_current(const oneWayList list);
boolean yoneWayList_goNext(oneWayList list);
boolean yoneWayList_goFirst(oneWayList list);
/*--- test */
boolean yoneWayList_isEmpty(const oneWayList list);
boolean yoneWayList_isFirst(const oneWayList list);
boolean yoneWayList_isLast(const oneWayList list);
boolean yoneWayList_hasNext(const oneWayList list);
g_reply yoneWayList_compareItem(const oneWayList list, 
    const void *anItem);
/*--- item */
void *yoneWayList_getItem(const oneWayList list);
void *yoneWayList_getItemClone(const oneWayList list, 
    const void *aControlObj);
boolean yoneWayList_addItem(oneWayList list, void *anItem);
boolean yoneWayList_deleteItem(oneWayList list);
/*--- provide customized function */
boolean yoneWayList_setFnItemClone(oneWayList list, 
    void *(*itemClone)(const void *anItem, const void *aControlObj));
boolean yoneWayList_setFnCompareItem(oneWayList list, 
    g_reply (*compareItem)(const void *anItem1, const void *anItem2));
/*--- function ONLY for internal use ---*/
oneWayListItem *yoneWayList_createOneWayListItem(oneWayList list, 
    void *anItem);
boolean yoneWayList_initFirstOneWayListItem(oneWayList list, 
    oneWayListItem *owLItem);

/*======= directly exposed function */
/** Create a new oneWayList.
<p>MUST provide a function to free an item of type <code>void *</code>.
The function is of the form <code>boolean (*freeItem)(void **)</code>.
</p>
@param freeItem the function to free an item of type <code>void *</code>.
@return a newly created and empty oneWayList, 
  or <code>NULL</code> if failure.
*/
oneWayList yoneWayList_create(boolean (*freeItem)(void **anItemP)) {
  if (freeItem == NULL) {return NULL;}
  oneWayList list;
  FN_MALLOC_ERRMSG(list, sizeof(struct oneWayList), 
      "Error: yoneWayList_create(boolean (*freeItem)(void **)) malloc(list)")
  if (list == NULL) {return NULL;}
  FN_MALLOC_ERRMSG(list->d, sizeof(struct oneWayListData), 
      "Error: yoneWayList_create(boolean (*freeItem)(void **)) malloc(list->d)")
  if (list->d == NULL) {yoneWayList_free(&list); return NULL;}
  /*--- data ---*/
  list->d->n = 0;
  list->d->first = NULL;
  list->d->prev = NULL;
  list->d->cur = NULL;
  list->d->sizeof_oneWayListItem = sizeof(struct oneWayListItem);
  /*--- init function pointers ---*/
  list->d->freeItem = freeItem;
  list->d->itemClone = NULL;
  list->d->compareItem = NULL;
  list->free = yoneWayList_free;
  list->size = yoneWayList_size;
  list->clear = yoneWayList_clear;
  /*--- traverse */
  list->previous = yoneWayList_previous;
  list->current = yoneWayList_current;
  list->goNext = yoneWayList_goNext;
  list->goFirst = yoneWayList_goFirst;
  /*--- test */
  list->isEmpty = yoneWayList_isEmpty;
  list->isFirst = yoneWayList_isFirst;
  list->isLast = yoneWayList_isLast;
  list->hasNext = yoneWayList_hasNext;
  list->compareItem = yoneWayList_compareItem;
  /*--- item */
  list->getItem = yoneWayList_getItem;
  list->getItemClone = yoneWayList_getItemClone;
  list->addItem = yoneWayList_addItem;
  list->deleteItem = yoneWayList_deleteItem;
  /*--- provide customized function */
  list->setFnItemClone = yoneWayList_setFnItemClone;
  list->setFnCompareItem = yoneWayList_setFnCompareItem;
  return list;
}

/** Create a new oneWayList with this item.
<p>MUST provide a function to free an item of type <code>void *</code>.
The function is of the form <code>boolean (*freeItem)(void **)</code>.
</p>
@param anItem the item to be added.
@param freeItem the function to free an item of type <code>void *</code>.
@return a newly created oneWayList with item, 
  or <code>NULL</code> if failure.
*/
oneWayList yoneWayList_createWithItem(void *anItem, 
    boolean (*freeItem)(void **anItemP)) {
  oneWayList list = yoneWayList_create(freeItem);
  if (list != NULL) {
    yoneWayList_initFirstOneWayListItem(list, 
      yoneWayList_createOneWayListItem(list, anItem));
  }
  return list;
}

/*======= exposed through function pointers */
/** Free this oneWay List from memory.
<p><b>WARNING:</b> Function will free items in the one way list as well, 
via the call to <code>boolean (*freeItem)(void **)</code>.
</p>
<p>Do NOT free anything taken out of item, 
e.g. via function pointer call to
<code>void *(*getItem)(const oneWayList)</code>.
Because the one way list still points to the item. 
Thus the item in a sense still sits inside the list so
it's memory will be free by this function.
</p>
<p>However you can free the item IF 
<ul>
<li>(1) the item's library say so, such as the item's "free" function 
which is our function pointer <code>boolean (*freeItem)(void **)</code> 
couldn't and doesn't handle it.
Thus requiring external program to handle this.
e.g. Cloned data struct (which it doesn't keep track of).
</li>
<li>(2) You SHOULD free this "cloned" copy of the item cloned from the list.
The list does NOT keep track of these "clones" and shouldn't release them..
<br/>UNLESS the item does some trick to <b>keep track of it's own clone</b>, 
AND also release them via the same function pointer call to 
<code>boolean (*freeItem)(void **)</code>.
<br/><b>Advice:</b> Read the item's library for details.
</li>
</ul>
</p>
@param listP pass in the address of oneWay List struct pointer to be free.
@return <code>true</code> if successful otherwise <code>false</code>.
*/
boolean yoneWayList_free(oneWayList *listP) {
  /* NOT critical if list is NULL, assume all's fine */
  if (*listP == NULL) {return true;}
  if ((*listP)->d != NULL) {
    /* NOT critical if unable to clear list, assume all's fine */
    yoneWayList_clear(*listP);
    /* free the oneWayListData struct */
    free((*listP)->d);
    (*listP)->d = NULL;
  } /* if ((*listP)->d != NULL) */
  /* free the oneWayList struct */
  free(*listP);
  *listP = NULL;
  return true;
}

/** Returns the number of elements in this list.
@param list the one way list.
@return Returns the number of elements in this list or -1 if failure.
*/
int yoneWayList_size(const oneWayList list) {
  if (list == NULL || list->d == NULL) {
    return -1;
  }
  return list->d->n;
}

/** Removes all of the elements from this list.
@param list the one way list.
@return <code>true</code> if successful 
  otherwise <code>false</code> nothing to remove.
*/
boolean yoneWayList_clear(oneWayList list) {
  if (list == NULL || list->d == NULL) {
    return false;
  }
  /* go through the whole list */
  list->d->cur = list->d->first;
  while (list->d->cur != NULL) {
    /* prepare */
    list->d->prev = list->d->cur;
    /* MUST move current to next item 1st! before freeing memory */
    list->d->cur = list->d->cur->next;
    /* free one way list item's content */
    if (list->d->freeItem(&list->d->prev->anItem) == false) {
      /* possible memory leak */
      perror("Error: yoneWayList_free(oneWayList *) list->d->freeItem(&list->d->prev->anItem)");
    }
    /* free one way list item, itself i.e. oneWayListItem struct */
    free(list->d->prev);
  }
  list->d->n = 0;
  list->d->first = NULL;
  list->d->prev = NULL;
  list->d->cur = NULL;
  return true;
}

/*--- traverse */
/** If the list previously point at something.
Mainly for debugg purpose.
@param list the one way list.
@return <code>true</code> if successful otherwise <code>false</code>.
*/
boolean yoneWayList_previous(const oneWayList list) {
  /* previously must point to something */
  if (list == NULL || list->d == NULL || list->d->prev == NULL) {
    return false;
  }
  return true;
}

/** If the list currently point at something.
@param list the one way list.
@return <code>true</code> if successful otherwise <code>false</code>.
*/
boolean yoneWayList_current(const oneWayList list) {
  /* currently must point to something */
  if (list == NULL || list->d == NULL || list->d->cur == NULL) {
    return false;
  }
  return true;
}

/** Move to next item. Successful if next in list point at something, 
otherwise stay at the tail of the list.
@param list the one way list.
@return <code>true</code> if successful otherwise <code>false</code>.
*/
boolean yoneWayList_goNext(oneWayList list) {
  if (list == NULL || list->d == NULL || list->d->cur == NULL 
    || list->d->cur->next == NULL) {
    return false;
  }
  list->d->prev = list->d->cur;
  list->d->cur = list->d->cur->next;
  return true;
}

/** Move to the first item in the list. 
@param list the one way list.
@return <code>true</code> if successful otherwise <code>false</code>.
*/
boolean yoneWayList_goFirst(oneWayList list) {
  if (list == NULL || list->d == NULL || list->d->first == NULL) {
    return false;
  }
  list->d->prev = list->d->cur;
  list->d->cur = list->d->first;
  return true;
}

/*--- test */
/** Tests if this list has no elements.
@param list the one way list.
@return <code>true</code> if successful otherwise <code>false</code>.
*/
boolean yoneWayList_isEmpty(const oneWayList list) {
  /* currently must point to something */
  if (list == NULL || list->d == NULL || list->d->cur == NULL) {
    return true;
  }
  return false;
}

/** Are we currently pointing to the first item in the list?
@param list the one way list.
@return true if pointing to the first item, false otherwise.
*/
boolean yoneWayList_isFirst(const oneWayList list) {
  if (list == NULL || list->d == NULL || list->d->cur == NULL) {
    return false;
  }
  if (list->d->cur == list->d->first) {
    return true;
  }
  return false;
}

/** Are we currently pointing to the last item in the list?
@param list the one way list.
@return true if pointing to the last item, false otherwise.
*/
boolean yoneWayList_isLast(const oneWayList list) {
  if (list == NULL || list->d == NULL || list->d->cur == NULL) {
    return false;
  }
  if (list->d->cur->next == NULL) {
    return true;
  }
  return false;
}

/** Is there another item after current item in the list?
@param list the one way list.
@return true if successful, false otherwise.
*/
boolean yoneWayList_hasNext(const oneWayList list) {
  if (list == NULL || list->d == NULL || list->d->cur == NULL
    || list->d->cur->next == NULL) {
    return false;
  }
  return true;
}

/** Compare a given item against the item currently 
pointing to within in the list.
<p>MUST provide the function of the form 
<code>g_reply (*compareItem)(const void *, const void *)</code>.
through <code>setFnCompareItem(..)</code> beforehand to 
able to use this function
</p>
<p><code>compareItem</code> should return <code>g_replyEqual</code> 
if <code>anItem</code> and list's item are equal.
Returns <code>g_replyLesser</code> or <code>g_replyGreater</code> 
if <code>anItem</code> is less than or greater than 
list's item respectively.
</p>
<p>This is a customized function provided by the item's library 
and is NOT compulsory.
</p>
@param list the one way list.
@param anItem the given item to compare 
  against the current item within the list.
@return Return <code>g_replyEqual</code> 
  if <code>anItem</code> and list's item are equal.
  Returns <code>g_replyLesser</code> or <code>g_replyGreater</code> 
  if <code>anItem</code> is less than or greater than 
  list's item respectively.
  Otherwise <code>g_replyFail</code> for failure.
*/
g_reply yoneWayList_compareItem(const oneWayList list, 
    const void *anItem) {
  if (list == NULL || list->d == NULL || list->d->cur == NULL
      || list->compareItem == NULL) {
    return g_replyFail;
  }
  /* the 1st parameter MUST be the given item, 
    2nd parameter MUST be the item from the list */
  return list->d->compareItem(anItem, list->d->cur->anItem);
}

/*--- item */
/** Obtain the item of the currently pointing item in the list. 
<p><b>WARNING:</b> Function will free items in the one way list as well, 
via the call to <code>boolean (*freeItem)(void **)</code>.
</p>
<p>Do NOT free anything taken out of item, 
e.g. via function pointer call to
<code>void *(*getItem)(const oneWayList)</code>.
Because the one way list still points to the item. 
Thus the item in a sense still sits inside the list so
it's memory will be free by this function.
</p>
<p>However you can free the item IF 
<ul>
<li>(1) the item's library say so, such as the item's "free" function 
which is our function pointer <code>boolean (*freeItem)(void **)</code> 
couldn't and doesn't handle it.
Thus requiring external program to handle this.
e.g. Cloned data struct (which it doesn't keep track of).
</li>
<li>(2) You SHOULD free this "cloned" copy of the item cloned from the list.
The list does NOT keep track of these "clones" and shouldn't release them..
<br/>UNLESS the item does some trick to <b>keep track of it's own clone</b>, 
AND also release them via the same function pointer call to 
<code>boolean (*freeItem)(void **)</code>.
<br/><b>Advice:</b> Read the item's library details.
</li>
</ul>
</p>
@param list the one way list.
@return the currently pointing item in the list.
*/
void *yoneWayList_getItem(const oneWayList list) {
  if (list == NULL || list->d == NULL || list->d->cur == NULL) {
    return NULL;
  }
  return list->d->cur->anItem;
}

/** Obtain a "cloned" copy of the item currently pointing to in the list. 
<p>This is a customized function provided by the item's library 
and is NOT compulsory.
</p>
<p>The customized function is given to the list via function pointer
<code>boolean (*setFnItemClone)(oneWayList, 
    void *(*itemClone)(const void *, const void *))</code>.
</p>
<p>You SHOULD free this "cloned" copy of the item cloned from the list.
The list does NOT keep track of these "clones" and shouldn't release them..
<br/>UNLESS the item does some trick to <b>keep track of it's own clone</b>, 
AND also release them via the same function pointer call to 
<code>boolean (*freeItem)(void **)</code>.
<br/><b>Advice:</b> Read the item's library for details.
</p>
@param list the one way list.
@param aControlObj an object that customized function may work with,
  e.g. provide cloning ability, other facility and perform other duty.
@return a "cloned" copy of the item currently pointing to in the list, or
  <code>NULL</code> if failure.
*/
void *yoneWayList_getItemClone(const oneWayList list, const void *aControlObj) {
  if (list == NULL || list->d == NULL || list->d->itemClone == NULL
    || list->d->cur == NULL) {
    return NULL;
  }
  return list->d->itemClone(list->d->cur->anItem, aControlObj);
}

/** Add this item after the current one in the list.
Allow item to be <code>NULL</code>.
<p><b>WARNING:</b> once the item is added into this one way list.
The one way list will handle freeing the item's memory, 
via call to function pointer <code>boolean (*freeItem)(void **)</code>.
</p>
<p><b>NEVER release memory of the item, once you've added into the list, 
UNLESS your copy is a clone of the one inside the list.</b> 
You have to handle freeing memory of the clone.
The list will handle freeing the item it is pointing to.
</p>
<p>The 2 possibility of item memory will be release by the one way list are 
when (1) the item is deleted via call to function pointer
<code>boolean (*deleteItem)(oneWayList)</code>,
or (2) freeing one way list memory via call to function pointer
<code>boolean (*free)(oneWayList *)</code>.
</p>
@param list the one way list.
@param anItem the item to be added.
@return true if successfully added the item, false otherwise.
*/
boolean yoneWayList_addItem(oneWayList list, void *anItem) {
  if (list == NULL || list->d == NULL) {return false;}
  oneWayListItem *owLItem = yoneWayList_createOneWayListItem(list, anItem);
  if (owLItem == NULL) {return false;}
  if (list->d->cur == NULL) {
    /* this is the first item in the list! */
    return yoneWayList_initFirstOneWayListItem(list, owLItem);
  }
  /* remember next item */
  owLItem->next = list->d->cur->next;
  /* add new item after current */
  list->d->cur->next = owLItem;
  /* new item becomes what we point to now */
  list->d->cur = owLItem;
  list->d->n++;
  return true;
}

/** Delete the current item from the list. 
After deletion the new current item will become the next item if it exist, 
otherwise the new current item will become the item in front if it exist.
If no item exist in front or to the back, the list is empty, 
current has no item and points to <code>NULL</code>.
<p>WARNING: There is speed penalty, if deleting item at end of list 
since after the deletion the list will traverse from start 
to re-position itself (the current pointer) at the end of list.
</p>
<p>NOTE: One way list is made for adding unordered short list, 
with small foot print and using simple loop to retrieve its' items. 
If individual item deletion is required recommend to use other library. 
</p>
@param list the one way list.
@return <code>true</code> if successfully otherwise <code>false</code>.
*/
boolean yoneWayList_deleteItem(oneWayList list) {
  if (list == NULL || list->d == NULL) {return false;}
  if (list->d->cur == NULL) {return false;}
  oneWayListItem *owLItemToDelete = list->d->cur;
  if (list->d->cur == list->d->first) {
    /* next item becomes first item */
    list->d->first = list->d->cur->next;
  } else {
    /* previous item points to current's next item */
    list->d->prev->next = list->d->cur->next;
    list->d->cur = list->d->prev->next;
  }
  /* free one way list item's content */
  if (list->d->freeItem(&owLItemToDelete->anItem) == false) {
    /* possible memory leak */
    perror("Error: yoneWayList_deleteItem(oneWayList) list->d->freeItem(&owLItemToDelete->anItem) == false");
  }
  /* free one way list item, itself */
  free(owLItemToDelete);
  list->d->n--;
  /* re-position cur pointer? 
    NB: speed penalty */
  if (list->d->first != NULL && list->d->cur == NULL) {
    list->d->cur = list->d->first;
    while (list->d->cur->next != NULL) {
      list->d->prev = list->d->cur;
      list->d->cur = list->d->cur->next;
    }
  }
  return true;
}

/*--- provide customized function */
/** Provide a function to clone an item of type <code>void *</code>.
The function is of the form 
<code>void *(*itemClone)(const void *, const void *)</code>.
<p>This is a customized function provided by the item's library 
and is NOT compulsory.
</p>
<p>You SHOULD free this "cloned" copy of the item cloned from the list.
The list does NOT keep track of these "clones" and shouldn't release them..
<br/>UNLESS the item does some trick to <b>keep track of it's own clone</b>, 
AND also release them via the same function pointer call to 
<code>boolean (*freeItem)(void **)</code>.
<br/><b>Advice:</b> Read the item's library for details.
</p>
@param list the one way list.
@param itemClone the function to clone an item of type <code>void *</code>.
@return <code>true</code> if successful otherwise <code>false</code>.
*/
boolean yoneWayList_setFnItemClone(oneWayList list, 
    void *(*itemClone)(const void *anItem, const void *aControlObj)) {
  if (list == NULL || list->d == NULL || itemClone == NULL) {
    return false;
  }
  list->d->itemClone = itemClone;
  return true;
}

/** Provide a function to compare two items of type <code>void *</code>.
The function is of the form 
<code>g_reply (*compareItem)(const void *, const void *)</code>.
<p>This function will be used for comparing a given item against 
the item currently pointing to within in the list.
<br/><code>anItem1</code> is the given item.
<br/><code>anItem2</code> is the current item within the list.
</p>
<p><code>compareItem</code> should return <code>g_replyEqual</code> 
if <code>anItem1</code> and <code>anItem2</code> are equal.
Returns <code>g_replyLesser</code> or <code>g_replyGreater</code> 
if <code>anItem1</code> is less than or greater than 
<code>anItem2</code> respectively.
</p>
<p>This is a customized function provided by the item's library 
and is NOT compulsory.
</p>
@param list the one way list.
@param compareItem the function to compare 
  two items of type <code>void *</code>.
@return <code>true</code> if successful otherwise <code>false</code>.
*/
boolean yoneWayList_setFnCompareItem(oneWayList list, 
    g_reply (*compareItem)(const void *anItem1, const void *anItem2)) {
  if (list == NULL || list->d == NULL || compareItem == NULL) {
    return false;
  }
  list->d->compareItem = compareItem;
  return true;
}

/*======= function ONLY for internal use */
/** Initialize the empty list with the 1st item.
This function is for internal use ONLY, do NOT expose this.
@param list the one way list.
@param owLItem the one way list item.
@return <code>true</code> if successful otherwise <code>false</code>.
*/
boolean yoneWayList_initFirstOneWayListItem(oneWayList list, 
    oneWayListItem *owLItem) {
  if (list == NULL || list->d == NULL) {return false;}
  if (owLItem == NULL) {return false;}
  list->d->first = owLItem;
  list->d->cur = owLItem;
  list->d->n++;
  return true;
}

/** Create a new oneWayListItem with this item.
Allow item to be <code>NULL</code>.
This function is for internal use ONLY, do NOT expose this.
@param list the one way list.
@param anItem the item to be added.
@return a newly created oneWayListItem with item, or <code>NULL</code> if failure.
*/
oneWayListItem *yoneWayList_createOneWayListItem(oneWayList list, 
    void *anItem) {
  oneWayListItem *owLItem;
  FN_MALLOC_ERRMSG(owLItem, list->d->sizeof_oneWayListItem, 
      "Error: yoneWayList_createOneWayListItem(oneWayList, void *) malloc(owLItem)")
  if (owLItem == NULL) {return NULL;}
  owLItem->next = NULL;
  owLItem->anItem = anItem;
  return owLItem;
}
